package com.yanyu.space.common.redis;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import com.yanyu.space.common.core.bootlog.BootstrapLogContext;
import com.yanyu.space.common.core.constants.SpaceConstants;
import com.yanyu.space.common.core.yaml.YamlPropertySourceFactory;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * @author yanyu
 */
@Configuration
@PropertySource(factory = YamlPropertySourceFactory.class, value = "classpath:" + SpaceConstants.Properties.REDIS_PROP)
public class RedisAutoConfig {

    @Bean("stringobjectRedisTemplate")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        ObjectMapper objectMapper = new ObjectMapper();
        // 日期序列化处理LocalDateTime序列化和反序列化
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        objectMapper.registerModule(new Jdk8Module())
                .registerModule(new JavaTimeModule())
                .registerModule(new ParameterNamesModule());
        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(objectMapper);
        redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
        redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }


    @Configuration
    public static class RedisBootstrapLog implements EnvironmentAware {
        private static final String REDIS_PROTOCOL_PREFIX = "redis://";

        @Override
        public void setEnvironment(Environment environment) {
            String bootstrapLogMsg = "";
            Method clusterMethod = ReflectionUtils.findMethod(RedisProperties.class, "getCluster");
            RedisProperties redisProperties = Binder.get(environment).bind("spring.redis", RedisProperties.class).get();
            if (redisProperties.getSentinel() != null) {
                bootstrapLogMsg = String.format("开启redis功能, 采用哨兵模式, 连接地址 -> %s", redisProperties.getSentinel().getMaster());
            } else if (clusterMethod != null && ReflectionUtils.invokeMethod(clusterMethod, redisProperties) != null) {
                Object clusterObject = ReflectionUtils.invokeMethod(clusterMethod, redisProperties);
                Method nodesMethod = ReflectionUtils.findMethod(clusterObject.getClass(), "getNodes");
                List<String> nodesObject = (List) ReflectionUtils.invokeMethod(nodesMethod, clusterObject);
                String[] nodes = convert(nodesObject);
                bootstrapLogMsg = String.format("开启redis功能, 采用集群模式, 连接地址 -> %s", StringUtils.join(nodes, ";"));
            } else {
                bootstrapLogMsg = String.format("开启redis功能, 采用单节点模式, 连接地址 -> %s", redisProperties.getHost() + ":" + redisProperties.getPort());
            }
            BootstrapLogContext.put("redis", bootstrapLogMsg);
        }

        private String[] convert(List<String> nodesObject) {
            List<String> nodes = new ArrayList<String>(nodesObject.size());
            for (String node : nodesObject) {
                if (!node.startsWith(REDIS_PROTOCOL_PREFIX)) {
                    nodes.add(REDIS_PROTOCOL_PREFIX + node);
                } else {
                    nodes.add(node);
                }
            }
            return nodes.toArray(new String[nodes.size()]);
        }
    }
}
